package com.example.demo.service.impl;

import com.example.demo.account.AccountInfo;
import com.example.demo.entity.AccountEntity;
import com.example.demo.repository.AccountRepository;
import com.example.demo.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.crypto.keypair.ECDSAKeyPair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 账户服务实现类，提供区块链账户管理功能
 */
@Slf4j
@Service
public class AccountServiceImpl implements AccountService {
    
    @Resource
    private Client client;
    
    @Autowired
    private AccountRepository accountRepository;
    
    @Value("${fisco.bcos.account.hexPrivateKey}")
    private String defaultPrivateKey;
    
    // 内存缓存，提高查询性能
    private final Map<String, AccountInfo> accountCache = new HashMap<>();
    
    /**
     * 初始化默认账户并加载已有账户到缓存
     */
    @PostConstruct
    public void init() {
        try {
            // 1. 加载默认账户
            initDefaultAccount();
            
            // 2. 加载所有账户到缓存
            loadAllAccountsToCache();
            
        } catch (Exception e) {
            log.error("初始化账户服务失败", e);
        }
    }
    
    /**
     * 初始化默认账户
     */
    private void initDefaultAccount() {
        if (defaultPrivateKey != null && !defaultPrivateKey.isEmpty()) {
            AccountInfo defaultAccount = loadAccountFromPrivateKey(defaultPrivateKey);
            
            // 设置为默认账户
            AccountEntity entity = accountRepository.findById(defaultAccount.getAddress()).orElse(null);
            if (entity != null) {
                entity.setIsDefault(true);
                accountRepository.save(entity);
                log.info("已初始化默认账户: {}", defaultAccount.getAddress());
            }
        }
    }
    
    /**
     * 加载所有账户到缓存
     */
    private void loadAllAccountsToCache() {
        List<AccountEntity> accounts = accountRepository.findAll();
        for (AccountEntity account : accounts) {
            AccountInfo accountInfo = entityToInfo(account);
            accountCache.put(account.getAddress(), accountInfo);
        }
        log.info("已加载 {} 个账户到缓存", accounts.size());
    }
    
    /**
     * 将实体转换为账户信息对象
     */
    private AccountInfo entityToInfo(AccountEntity entity) {
        return new AccountInfo(
            entity.getAddress(),
            entity.getPublicKey(),
            entity.getPrivateKey()
        );
    }
    
    /**
     * 创建新账户
     * @return AccountInfo 账户信息
     */
    @Override
    @Transactional
    public AccountInfo createAccount() {
        try {
            // 创建ECDSA密钥对
            ECDSAKeyPair keyPair = new ECDSAKeyPair();
            CryptoKeyPair cryptoKeyPair = keyPair.generateKeyPair();
            
            // 构建账户信息
            AccountInfo accountInfo = new AccountInfo(
                cryptoKeyPair.getAddress(),
                cryptoKeyPair.getHexPublicKey(),
                cryptoKeyPair.getHexPrivateKey()
            );
            
            // 保存到数据库和缓存
            saveAccount(accountInfo);
            
            log.info("创建新账户: {}", accountInfo.getAddress());
            return accountInfo;
            
        } catch (Exception e) {
            log.error("创建账户失败", e);
            throw new RuntimeException("账户创建失败", e);
        }
    }
    
    /**
     * 保存账户信息到数据库和缓存
     */
    private void saveAccount(AccountInfo accountInfo) {
        // 保存到数据库
        AccountEntity entity = new AccountEntity(
            accountInfo.getAddress(),
            accountInfo.getPublicKey(),
            accountInfo.getPrivateKey()
        );
        accountRepository.save(entity);
        
        // 保存到缓存
        accountCache.put(accountInfo.getAddress(), accountInfo);
    }
    
    /**
     * 从私钥加载账户
     * @param privateKey 私钥
     * @return AccountInfo 账户信息
     */
    @Override
    @Transactional
    public AccountInfo loadAccountFromPrivateKey(String privateKey) {
        try {
            CryptoKeyPair cryptoKeyPair = client.getCryptoSuite().createKeyPair(privateKey);
            
            AccountInfo accountInfo = new AccountInfo(
                cryptoKeyPair.getAddress(),
                cryptoKeyPair.getHexPublicKey(),
                privateKey
            );
            
            // 检查数据库中是否已存在
            if (!accountRepository.existsById(accountInfo.getAddress())) {
                saveAccount(accountInfo);
            }
            
            // 保存到缓存
            accountCache.put(accountInfo.getAddress(), accountInfo);
            
            log.info("从私钥加载账户: {}", accountInfo.getAddress());
            return accountInfo;
            
        } catch (Exception e) {
            log.error("从私钥加载账户失败", e);
            throw new RuntimeException("加载账户失败", e);
        }
    }
    
    /**
     * 获取账户信息
     * @param address 账户地址
     * @return AccountInfo 账户信息，如果不存在则返回null
     */
    @Override
    public AccountInfo getAccount(String address) {
        // 先从缓存获取
        AccountInfo accountInfo = accountCache.get(address);
        
        // 如果缓存不存在，则从数据库获取
        if (accountInfo == null) {
            AccountEntity entity = accountRepository.findById(address).orElse(null);
            if (entity != null) {
                accountInfo = entityToInfo(entity);
                // 添加到缓存
                accountCache.put(address, accountInfo);
            }
        }
        
        return accountInfo;
    }
    
    /**
     * 检查账户是否存在
     * @param address 账户地址
     * @return boolean 是否存在
     */
    @Override
    public boolean accountExists(String address) {
        // 先检查缓存
        if (accountCache.containsKey(address)) {
            return true;
        }
        // 再检查数据库
        return accountRepository.existsById(address);
    }
    
    /**
     * 检查账户在链上是否存在（有交易记录）
     * @param address 账户地址
     * @return 链上是否存在
     */
    @Override
    public boolean checkAccountOnChain(String address) {
        try {
            // 检查地址格式
            if (address == null || !address.startsWith("0x") || address.length() != 42) {
                log.warn("地址格式不正确: {}", address);
                return false;
            }
            
            // 检查链接是否正常
            BigInteger blockNumber = client.getBlockNumber().getBlockNumber();
            log.info("链上检查 - 当前区块高度: {}, 地址: {}", blockNumber, address);
            
            // 在FISCO BCOS中，所有格式正确的地址都是有效的（无需预创建）
            return address.matches("^0x[0-9a-fA-F]{40}$");
        } catch (Exception e) {
            log.warn("链上账户检查失败: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 获取账户，如果不存在则创建
     * @param address 账户地址
     * @return AccountInfo 账户信息
     */
    @Override
    @Transactional
    public AccountInfo getOrCreateAccount(String address) {
        AccountInfo accountInfo = getAccount(address);
        if (accountInfo == null) {
            log.info("账户不存在，创建新账户: {}", address);
            accountInfo = createAccount();
        }
        return accountInfo;
    }
    
    /**
     * 获取所有账户
     * @return 账户列表
     */
    @Override
    public List<AccountEntity> getAllAccounts() {
        return accountRepository.findAllByOrderByCreateTimeDesc();
    }
    
    /**
     * 获取默认账户
     * @return 默认账户
     */
    @Override
    public AccountInfo getDefaultAccount() {
        List<AccountEntity> defaultAccounts = accountRepository.findByIsDefaultTrue();
        if (defaultAccounts.isEmpty()) {
            return null;
        }
        
        return entityToInfo(defaultAccounts.get(0));
    }
    
    /**
     * 设置默认账户
     * @param address 账户地址
     */
    @Override
    @Transactional
    public void setDefaultAccount(String address) {
        // 先将所有账户设为非默认
        List<AccountEntity> defaultAccounts = accountRepository.findByIsDefaultTrue();
        for (AccountEntity account : defaultAccounts) {
            account.setIsDefault(false);
            accountRepository.save(account);
        }
        
        // 设置新的默认账户
        AccountEntity entity = accountRepository.findById(address).orElse(null);
        if (entity != null) {
            entity.setIsDefault(true);
            accountRepository.save(entity);
            log.info("设置默认账户: {}", address);
        } else {
            throw new RuntimeException("账户不存在: " + address);
        }
    }
} 